package cn.yj.core.business.service;

import cn.yj.annotation.pagehelper.page.Page;
import cn.yj.cgs.dba.entity.Message;
import cn.yj.cgs.dba.entity.Task;
import cn.yj.cgs.dba.entity.UserTask;
import cn.yj.cgs.dba.entity.vo.TaskView;
import cn.yj.cgs.dba.entity.vo.TaskVo;
import cn.yj.cgs.dba.mapper.MyTaskMapper;
import cn.yj.cgs.dba.mapper.TaskMapper;
import cn.yj.common.Enums;
import cn.yj.common.UUIdUtils;
import cn.yj.common.lock.reentrant.SyncLocalIVariableLock;
import cn.yj.commons.utils.StringUtils;
import cn.yj.config.SchoolCBDConfig;
import cn.yj.core.business.service.iservice.IMessageService;
import cn.yj.core.business.service.iservice.ITaskService;
import cn.yj.cgs.dba.entity.vo.TaskCommonVo;
import cn.yj.core.kwfilter.KeyWorkFilterService;
import cn.yj.core.task.delay.task.TaskHandler;
import cn.yj.entity.R;
import cn.yj.params.check.annotation.CheckObjectValue;
import cn.yj.params.check.annotation.KeyValue;
import cn.yj.params.check.annotation.Require;
import cn.yj.tools.exception.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static cn.yj.commons.utils.FilterNull.isNull;

/**
 * 任务
 *
 * @author 永健
 * @since 2021-04-19 16:44
 */
@Service
public class TaskService extends ServiceImpl<TaskMapper, Task> implements ITaskService<Task> {

    /**
     * 默认积分
     */
    @Resource
    private SchoolCBDConfig schoolCBDConfig;

    @Resource
    private CustomerService customerService;

    @Resource
    private IntegralService integralService;

    @Resource
    private IMessageService messageService;

    @Resource
    private MyTaskMapper userTaskMapper;


    private static final ConcurrentHashMap<String, Integer> PUBLISHER_CODE_TASK_COUNT = new ConcurrentHashMap();
    private static final ConcurrentHashMap<String, Integer> RECEIVER_CODE_TASK_COUNT = new ConcurrentHashMap();

    private static SyncLocalIVariableLock LOCK = new SyncLocalIVariableLock();

    /**
     * 记录当天的做任务用户
     * 用于判断当天用户是否已经进账一个文章积分
     */
    //@Scheduled(cron = "0 0 0 */1 * ?")
    @PostConstruct
    public void initData() {

        // 发布人
        List<Map<String, Object>> userCodes = baseMapper.selectPublisherCodeByDate(new Date());
        userCodes.forEach(map -> {
            String userCode = map.get("userCode").toString();
            int num = Integer.parseInt(map.get("num").toString());
            PUBLISHER_CODE_TASK_COUNT.put(userCode, num);
        });


        List<Map<String, Object>> maps = baseMapper.selectReceiveListByDate(new Date());
        maps.forEach(map -> {
            String userCode = map.get("userCode").toString();
            int num = Integer.parseInt(map.get("num").toString());
            RECEIVER_CODE_TASK_COUNT.put(userCode, num);
        });
    }

    @Override
    public Page<TaskView> pageList(Map<String, Object> map, Page<TaskView> page) {
        map.put("auditStatus", Enums.Article.STATUS.SUCCESS.name());
        baseMapper.findList(map, page);
        return page;
    }

    /**
     * 任务发布
     *
     * @param t 对象
     *
     * @return true/false
     */
    @CheckObjectValue(keyValue = @KeyValue(type = Task.class, name = {"effectivTime", "userCode", "content",
            "urgentState"}))
    @Override
    public R publish(TaskVo t) {
        // 检查是否完善了个人联系方式
        if (!customerService.authenticated(t.getUserCode())) {
            throw new ServiceException("请先填写联系方式哦~");
        }

        // 失效时间要大于1分钟
        if (t.getEffectivTime().getTime() - System.currentTimeMillis() < 60 * 1000) {
            throw new ServiceException("失效时间至少要大于1分钟");
        }

        Task task = new Task();
        BeanUtils.copyProperties(t, task);
        task.setAuditStatus(Enums.Article.STATUS.SUCCESS.name());

        // 敏感词过滤
        Set<String> filterWord = KeyWorkFilterService.filter_search_out_word(t.getContent(),
                customerService.getOpenIdByUserCode(t.getUserCode()));
        String errorMsg = "发布成功";

        if (!filterWord.isEmpty()) {
            task.setAuditStatus(Enums.Article.STATUS.DOING.name());
            errorMsg = notice(filterWord);
        }

        // 设置学校
        task.setSchoolName(customerService.getCustomerSchoolByCode(task.getUserCode()));
        // 默认属性设置
        task.setTaskStatus(Enums.TASK.STATUS.no.name());
        task.setTaskNo(UUIdUtils.getTaskNo());
        task.setIntegral(schoolCBDConfig.getIntegralConfig().getOther());
        task.setUrgentState(Enums.TASK.UrgentState.valueOf(t.getUrgentState()));
        task.setServiceFees(StringUtils.isNull(t.getServiceFees())? BigDecimal.ZERO : t.getServiceFees());
        boolean result = super.save(task);
        if (result) {
            TaskHandler.delayQueue(task.getTaskNo());
        }
        return result? R.success(errorMsg) : R.error();

    }

    private String notice(Set<String> words) {
        return "您的任务内容疑似存在敏感词 [ " + StringUtils.join(words, ",") + "],文章需要审核方可通过!  请同学文明用语哦～";
    }


    /**
     * 更新任务信息
     *
     * @param taskVo
     */
    @Override
    @CheckObjectValue(keyValue = @KeyValue(type = TaskVo.class, name = {"taskNo", "effectivTime", "urgentState",
            "userCode", "content"}))
    public R updateInfoById(TaskVo taskVo) {
        Task byTaskNo =
                getOne(new QueryWrapper<Task>().lambda().eq(Task::getTaskNo, taskVo.getTaskNo()).select(Task::getTaskStatus, Task::getAuditStatus));
        isNull(byTaskNo, String.format("任务不存在:%s", taskVo.getTaskNo()));

        // 失效时间要大于1分钟
        if (taskVo.getEffectivTime().getTime() - System.currentTimeMillis() < 60 * 1000) {
            throw new ServiceException("失效时间至少要大于1分钟");
        }


        String status = byTaskNo.getTaskStatus();
        Task task = new Task();
        BeanUtils.copyProperties(taskVo, task);
        task.setAuditStatus(Enums.Article.STATUS.SUCCESS.name());

        Set<String> filterWord = KeyWorkFilterService.filter_search_out_word(taskVo.getContent(),
                customerService.getOpenIdByUserCode(taskVo.getUserCode()));
        String errorMsg = "更新成功";
        if (!filterWord.isEmpty()) {
            task.setAuditStatus(Enums.Article.STATUS.DOING.name());
            errorMsg = notice(filterWord);
        }

        task.setUrgentState(Enums.TASK.UrgentState.valueOf(taskVo.getUrgentState()));
        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.no, status)) {
            boolean update = update(task, new UpdateWrapper<Task>().lambda().eq(Task::getTaskNo, task.getTaskNo()));
            if (update) {TaskHandler.delayQueue(task.getTaskNo());}
            ;
            return update? R.success(errorMsg) : R.error();
        }
        throw new ServiceException("当前任务不可修改");
    }


    /**
     * 修改任务状态
     * /取消/超时取消/接单/doing/finish
     *
     * @param taskNo 任务编号
     * @param status 状态
     */
    @Override
    public boolean updateTaskStatus(
            @Require String taskNo, @Require Enums.TASK.STATUS status) {
        boolean result = baseMapper.update(new Task().setTaskStatus(status.name()),
                new UpdateWrapper<Task>().lambda().eq(Task::getTaskNo, taskNo)) > Enums.Common.ZERO;
        if (!result) {
            throw new ServiceException("更新状态失败");
        }
        return true;
    }


    @Transactional
    @Override
    public boolean timeOutCancelTask(@Require String taskNo) {
        Task task = baseMapper.selectByNoForUpdate(taskNo);
        if (!task.getTaskStatus().equals(Enums.TASK.STATUS.no.name())) {
            // throw new ServiceException(String.format("订单状态已被更改为：%s,超时取消丢弃", task.getTaskStatus()));
            log.warn(String.format("订单状态已被更改为：%s,超时取消丢弃", task.getTaskStatus()));
            return false;
        }
        if (task.getEffectivTime().getTime() > System.currentTimeMillis()) {
            // 不取消,失效时间被更改延长了
            return false;
        }
        messageService.notice(new Message("您的任务在规定时间内，无人接单，已超时取消～", "任务通知", task.getUserCode()));
        return this.updateTaskStatus(taskNo, Enums.TASK.STATUS.timeout);
    }

    @Override
    public TaskVo getByTaskNo(@Require String taskNo) {
        Task one = super.getOne(new QueryWrapper<Task>().lambda().eq(Task::getTaskNo, taskNo));
        TaskVo taskVo = new TaskVo();
        if (one != null) {
            BeanUtils.copyProperties(one, taskVo);
        }
        return taskVo;
    }

    @Override
    public List<TaskVo> getTaskByStatus(@Require String status, String auditStatus) {
        return baseMapper.selectListByStatus(status, auditStatus);
    }

    @Override
    public List<TaskCommonVo> getNewestTask(String schoolName) {
        return baseMapper.selectNewestTaskAndPackage(schoolName);
    }

    /**
     * 接单
     *
     * @param userCode 接单的用户编号
     * @param taskNo   任务编号
     */
    @Transactional
    @Override
    public boolean toAgree(@Require String userCode, @Require String taskNo) {
        Task task = baseMapper.selectByNoForUpdate(taskNo);
        isNull(task, String.format("找不到改任务编号:%s", taskNo));
        if (task.getUserCode().equals(userCode)) {
            throw new ServiceException("不可以接自己的单子哦～");
        }
        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.no, task.getTaskStatus())) {
            if (this.updateTaskStatus(taskNo, Enums.TASK.STATUS.toAgree)) {
                UserTask userTask = new UserTask();
                BeanUtils.copyProperties(task, userTask);
                userTask.setTaskStatus(Enums.TASK.STATUS.toAgree.name());
                userTask.setMyUserCode(userCode);
                userTask.setTaskNo(taskNo);
                userTask.setUserTaskNo(UUIdUtils.getCode());

                // 通知
                messageService.notice(new Message("有用户接单啦，快去看看吧～", "任务通知", task.getUserCode()));
                return userTaskMapper.insert(userTask) > 0;
            }
        }
        throw new ServiceException("当前订单无法接单");
    }


    @Transactional
    @Override
    public boolean agreeTask(@Require String taskNo) {
        Task task = baseMapper.selectByNoForUpdate(taskNo);
        isNull(task, String.format("找不到改任务编号:%s", taskNo));
        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.toAgree, task.getTaskStatus())) {
            if (this.updateTaskStatus(taskNo, Enums.TASK.STATUS.doing)) {
                messageService.notice(new Message("您接的任务，发布方已同意您接单，请查知", "任务通知",
                        userTaskMapper.selectMyUserCode(taskNo)));
                return updateUserTaskStatusByNo(taskNo, Enums.TASK.STATUS.doing.name());
            }
        }
        throw new ServiceException("当前订单无法更改");
    }

    @Transactional
    @Override
    public boolean disAgreeTask(@Require String userCode, @Require String taskNo) {
        Task task = baseMapper.selectByNoForUpdate(taskNo);
        isNull(task, String.format("找不到改任务编号:%s", taskNo));
        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.toAgree, task.getTaskStatus())) {
            if (this.updateTaskStatus(taskNo, Enums.TASK.STATUS.no)) {
                String msg = "用户取消的单子";
                if (userCode.equals(task.getUserCode())) {
                    userCode = userTaskMapper.selectMyUserCode(taskNo);
                    msg = "发布方已拒绝您接该单子";
                } else {
                    userCode = task.getUserCode();
                }
                messageService.notice(new Message(msg + "， 请查知", "任务通知", userCode));
                return updateUserTaskStatusByNo(taskNo, Enums.TASK.STATUS.cancel.name());
            }
        }
        throw new ServiceException("当前订单无法更改");
    }


    private boolean updateUserTaskStatusByNo(String taskNo, String status) {
        UserTask userTask = userTaskMapper.selectByNo(taskNo);
        userTask.setTaskStatus(status);
        if (userTaskMapper.updateById(userTask) > 0) {
            return true;
        }
        throw new ServiceException("更新状态失败");
    }

    @Transactional
    @Override
    public BigDecimal getIntegralByTaskNo(@Require String taskNo) {
        return baseMapper.selectIntegralByTaskNo(taskNo);
    }

    @Transactional
    @Override
    public boolean finishTask(@Require String userCode, @Require String taskNo) {
        Task task = baseMapper.selectByNoForUpdate(taskNo);
        String noticeUser = task.getUserCode();
        isNull(task, String.format("找不到改任务编号:%s", taskNo));
        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.doing, task.getTaskStatus())) {
            String acceptUserCode = userTaskMapper.selectMyUserCode(taskNo);
            if (this.updateTaskStatus(taskNo, Enums.TASK.STATUS.finish)) {
                // 更新积分表
                BigDecimal integral = getIntegralByTaskNo(taskNo);
                isNull(integral, String.format("找不到当前任务信息：%s", taskNo));

                // 接单人积分
                try {
                    LOCK.unlock(userCode);
                    Integer maxCount = RECEIVER_CODE_TASK_COUNT.get(acceptUserCode);
                    maxCount = StringUtils.isNull(maxCount)? Enums.Common.ZERO : maxCount;
                    if (maxCount < schoolCBDConfig.getIntegralConfig().getReceiverMaxCount()) {
                        integralService.updateIntegral(acceptUserCode, integral, true,
                                Enums.IntegralWater.SOURCE.REC_TASK, null);
                        ++maxCount;
                        RECEIVER_CODE_TASK_COUNT.put(acceptUserCode, maxCount);
                    }
                } finally {
                    LOCK.unlock(userCode);
                }


                // 发布人积分
                try {
                    LOCK.lock(userCode);
                    Integer maxCount = PUBLISHER_CODE_TASK_COUNT.get(acceptUserCode);
                    maxCount = StringUtils.isNull(maxCount)? Enums.Common.ZERO : maxCount;
                    if (maxCount < schoolCBDConfig.getIntegralConfig().getPublishMaxCount()) {
                        integralService.updateIntegral(task.getUserCode(), integral, true,
                                Enums.IntegralWater.SOURCE.PUSH_TASK, null);
                        ++maxCount;
                        PUBLISHER_CODE_TASK_COUNT.put(task.getUserCode(), maxCount);
                    }
                } finally {
                    LOCK.unlock(userCode);
                }

                if (userCode.equals(task.getUserCode())) {
                    noticeUser = acceptUserCode;
                }
                messageService.notice(new Message("恭喜！！！ 您的快递任务已完成～   请记得联系对戏方结账哦～", "任务通知", noticeUser));
                return updateUserTaskStatusByNo(taskNo, Enums.TASK.STATUS.finish.name());
            }
        }
        throw new ServiceException("当前任务不可结束");
    }

    @Transactional
    @Override
    public boolean cancelTask(@Require String taskNo) {
        Task task = baseMapper.selectByNoForUpdate(taskNo);
        isNull(task, String.format("找不到改任务编号:%s", taskNo));
        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.doing, task.getTaskStatus())) {
            throw new ServiceException("订单进行中无法取消");
        }

        if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.toAgree, task.getTaskStatus()) || Enums.TASK.STATUS.equals(Enums.TASK.STATUS.no, task.getTaskStatus())) {
            UserTask userPackage = userTaskMapper.selectByNo(taskNo);
            userPackage.setTaskStatus(Enums.TASK.STATUS.cancel.name());
            userTaskMapper.updateById(userPackage);

            messageService.notice(new Message("对方取消了任务，请查知", "任务通知", userPackage.getMyUserCode()));

            return this.updateTaskStatus(taskNo, Enums.TASK.STATUS.cancel);
        }
        throw new ServiceException("订单不可取消");
    }

    @Override
    @CheckObjectValue(keyValue = @KeyValue(type = Map.class, name = {"userCode"}))
    public Page<TaskView> getTaskListByUserCode(@Require Page<TaskView> page, @Require Map<String, Object> params) {
        baseMapper.findList(params, page);
        return page;
    }


    @Override
    public Page<UserTask> listMyDoTask(@Require Page<UserTask> page, Map<String, Object> params) {
        userTaskMapper.findList(params, page);
        return page;
    }

    @Transactional
    @Override
    public boolean delMyDoTask(@Require List<String> userTaskNos) {
        userTaskNos.forEach(uTaskNo -> {
            String status = userTaskMapper.selectStatusByNo(uTaskNo);
            if (StringUtils.isBlank(status)) {
                throw new ServiceException(String.format("找不到改任务编号：%s", uTaskNo));
            }
            // 当前订单不是进行中的话可以删除
            if (!Enums.TASK.STATUS.equals(Enums.TASK.STATUS.doing, status)) {
                userTaskMapper.delete(new UpdateWrapper<UserTask>().lambda().eq(UserTask::getUserTaskNo, uTaskNo));
                // 还原订单状态
                if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.toAgree, status)) {
                    updateTaskStatus(uTaskNo, Enums.TASK.STATUS.no);
                }
            } else {
                throw new ServiceException(String.format("订单进行中不可删除:%s", uTaskNo));
            }

        });
        return true;
    }

    @Transactional
    @Override
    public boolean delMyPushTask(@Require List<String> taskNos) {
        taskNos.forEach(taskNo -> {
            Task task = baseMapper.selectByNoForUpdate(taskNo);
            String status = task.getTaskStatus();
            if (StringUtils.isBlank(status)) {
                throw new ServiceException(String.format("找不到改任务编号：%s", taskNo));
            }
            // 当前订单不是进行中的话可以删除
            if (!Enums.TASK.STATUS.equals(Enums.TASK.STATUS.doing, status)) {
                baseMapper.delete(new UpdateWrapper<Task>().lambda().eq(Task::getTaskNo, taskNo));
                if (Enums.TASK.STATUS.equals(Enums.TASK.STATUS.toAgree, status)) {
                    // 如果已经被接单。将其状态改为取消
                    UserTask userTask = userTaskMapper.selectByNo(taskNo);
                    userTask.setTaskStatus(Enums.TASK.STATUS.cancel.name());
                    userTaskMapper.updateById(userTask);
                    messageService.notice(new Message("发布方删除了一个任务,请查知", "任务通知", userTask.getMyUserCode()));
                }
            } else {
                throw new ServiceException(String.format("订单进行中不可删除:%s", taskNo));
            }
        });
        return true;
    }

}
